//include the libraries
//#pragma comment(lib, "lua5.1.lib")
//#pragma comment(lib, "luabind.lib")
//#pragma comment(lib, "lua.lib")
//#pragma comment(lib, "lualib.lib")
#pragma warning (disable : 4786)

extern "C"
{
  #include <lua.h>
  #include <lualib.h>
  #include <lauxlib.h>
}

#include <string>
#include <iostream>
using namespace std;

//include the luabind headers. Make sure you have the paths set correctly
//to the lua, luabind and Boost files.
#include <luabind/luabind.hpp>
#include <luabind/operator.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/asio.hpp>

using namespace luabind;

#include "Entity.h"
#include "Miner.h"
#include "luahelperfunctions.h"
#include "ScriptedStateMachine.h"

boost::asio::io_service g_io;

void helper(luabind::object aclosure)
{
	if (aclosure.is_valid())
	{
		aclosure();
	}

}

void post(luabind::object aclosure)
{
	g_io.post( boost::bind( &helper, boost::ref( aclosure ) ) );
}

void run()
{
	g_io.run();
}

void stop()
{
	g_io.stop();
}

class gctest
{
public:
	gctest(){}

	void callback(luabind::object o)
	{
		o("hi~from c++");
	}

};

void register_gctest(lua_State* lua_state)
{
	module(lua_state)
		[
			class_<gctest>("gctest")
			.def(constructor<>())
			.def( "callback", &gctest::callback, dependency(_2,_1) )
			,
			def( "post", &post )
			,
			def( "run", &run )
			, 
			def( "stop", &stop )
		];

}


void RegisterScriptedStateMachineWithLua(lua_State* pLua)
{
  module(pLua)
    [
      class_<ScriptedStateMachine<Miner> >("ScriptedStateMachine")

        .def("ChangeState", &ScriptedStateMachine<Miner>::ChangeState)
        .def("CurrentState", &ScriptedStateMachine<Miner>::CurrentState)
        .def("SetCurrentState", &ScriptedStateMachine<Miner>::SetCurrentState)
    ];
}


void RegisterEntityWithLua(lua_State* pLua)
{
  module(pLua)
    [
      class_<Entity>("Entity")

        .def("Name", &Entity::Name)
        .def("ID", &Entity::ID)
    ];
}


void RegisterMinerWithLua(lua_State* pLua)
{
  module(pLua)
    [
      class_<Miner, bases<Entity> >("Miner")

        .def("GoldCarried", &Miner::GoldCarried)
        .def("SetGoldCarried", &Miner::SetGoldCarried)
        .def("AddToGoldCarried", &Miner::AddToGoldCarried)
        .def("Fatigued", &Miner::Fatigued)
        .def("DecreaseFatigue", &Miner::DecreaseFatigue)
        .def("IncreaseFatigue", &Miner::IncreaseFatigue)
        .def("GetFSM", &Miner::GetFSM)
    ];
}

inline std::string string_copy(const std::string& str1, const std::string& str2)
{
	return str1+str2;
}
typedef boost::function<std::string (std::string const&,std::string const&)> str_str_function_type;
str_str_function_type str_str_function;

template<typename FuncType>
void test_function(FuncType f)
{
	if (!f)
	{
		std::cout<<"get a function."<<std::endl;
	}
	else
	{
		std::cout<<"get a null."<<std::endl;
	}
}

void RegisterStringTest(lua_State* pLua)
{
	str_str_function=string_copy;
	test_function(NULL);
	test_function(str_str_function);
	module(pLua)
		[
			def("string_copy", &string_copy)		
		];
}

class shared_dumy: public boost::enable_shared_from_this<shared_dumy>
{
public:
	shared_dumy(){}

	char dumy_[1024000];
};
class test_callback
{
public:
	test_callback(luabind::object o)
		: o_(o)
	{

	}

	luabind::object callback(luabind::object o)
	{
		return o_( this, o );
	}

	luabind::object o_;
};

std::ostream& operator<<( std::ostream& os, const test_callback& tc)
{
	os<<"test_callback"<<std::endl;
	return os;
}

namespace luabind {

	template<class T>
	T* get_pointer(boost::shared_ptr<T>& p) 
	{
		return p.get(); 
	}

	template<class A>
	boost::shared_ptr<const A>* 
		get_const_holder(boost::shared_ptr<A>*)
	{
		return 0;
	}
}

void register_test_callback(lua_State* L)
{
	using namespace luabind;
	module(L)
		[
			class_<test_callback>("tc")
			.def(constructor< luabind::object >())
			.def("callback", &test_callback::callback )		
			.def(tostring(self))
			, 
			class_< shared_dumy, boost::shared_ptr< shared_dumy > >("dumy")
			.def( constructor<>() )

		];
}

int main()
{
  //create a lua state
  lua_State* pLua = lua_open();

  LuaExceptionGuard guard(pLua);

  //open the lua libaries - new in lua5.1
  luaL_openlibs(pLua);

  //open luabind
  open(pLua);

  //bind the relevant classes to Lua
  RegisterEntityWithLua(pLua);
  RegisterMinerWithLua(pLua);
  RegisterScriptedStateMachineWithLua(pLua);
  //RegisterStringTest(pLua);
  register_test_callback(pLua);
  register_gctest(pLua);


  //load and run the script
  RunLuaScript(pLua, "StateMachineScript.lua");
  system("pause");
  //create a miner
  Miner bob("bob");

  //grab the global table from the lua state. This will inlclude
  //all the functions and variables defined in the scripts run so far
  //(StateMachineScript.lua in this example)
  object states = globals(pLua);

  //ensure states is a table
  if (type(states) == LUA_TTABLE)
  {
    //make sure Bob's CurrentState object is set to a valid state.
    bob.GetFSM()->SetCurrentState(states["State_GoHome"]);

    //run him through a few update cycles
    for (int i=0; i<10; ++i)
    {
      bob.Update();
    }
  }
  system("pause");
  return 0;
}


